package com.algo.FJSP.GaForFJSP;

import com.algo.FJSP.common.DataManage;
import lombok.Data;
import lombok.extern.log4j.Log4j;

import java.util.ArrayList;
import java.util.Random;

/**
 * @Author: guoxing
 * @Email: gx_study2022@163.com
 * @Description: 算法主体，包括交叉、变异、选择等操作
 * @Date: 2024/6/29 12:46
 * @ClassName: GA
 **/
@Data
@Log4j
public class GA {
    //初始种群大小
    private int initialPopSize;
    //选择过程中，精英选择的余量，余下的采用竞标赛选择的方式进行选择
    private int eSize;
    //要进行锦标赛的个体数
    private int tNum=3;
    //交叉概率
    private double crossoverProbability;
    //变异概率
    private double mutationProbability;
    //最大迭代次数
    private int epochs;
    //最大计算时间ms
    private  long maxCalTime=36000;
    //最大忍耐无更优解的代数
    private int maxNoBetterNum;
    //
    private GAUtils gaUtils=new GAUtils();
    //产生随机数
    private final Random random=new Random();

    public GA(int initialPopSize, int eSize, double crossoverProbability, double mutationProbability, int epochs, long maxCalTime, int maxNoBetterNum) {
        this.initialPopSize = initialPopSize;
        this.eSize = eSize;
        this.crossoverProbability = crossoverProbability;
        this.mutationProbability = mutationProbability;
        this.epochs = epochs;
        this.maxCalTime = maxCalTime;
        this.maxNoBetterNum = maxNoBetterNum;
    }


    /**
     * @Description 调度主方法，在这个方法中进行循环获取最好的个体
     * @author guoxing
     * @date 2024/7/10 22:23
     * @param dataManage
     * @return Individual
     */
    public Individual solve(DataManage dataManage) throws Exception {
        Individual bestIndividual=null;
        int noBetterNum=0;
        long beginTime=System.currentTimeMillis();
        //生成初始种群
        ArrayList<Individual> individuals=gaUtils.generateInitialPopulation(dataManage,initialPopSize);
        //开始迭代
        for (int i = 0; i < epochs; i++) {
//            System.out.println("=============第"+(i+1)+"代=============");
            log.info("=============第"+(i+1)+"代=============");
            //解码，得到适应的个体
            for(Individual individual:individuals){
                individual.setFitness(gaUtils.decode(individual,dataManage));
            }
            ArrayList<Individual> selectIndividuals=gaUtils.selection(individuals,eSize,tNum);
//            System.out.println("当代最优个体适应度值为："+selectIndividuals.get(0).getFitness());
            log.info("当代最优个体适应度值为："+selectIndividuals.get(0).getFitness());
            if (i==0){
                //第一代最佳个体是选择的最佳个体
                bestIndividual=selectIndividuals.get(0).clone();
            }else{
                //比较最佳个体与选择的最佳个体的适应度值，如果更好则替换，不好则不替换，忍耐次数+1
                //如果取到数则用这个
//                if(bestIndividual.getFitness()<selectIndividuals.get(0).getFitness()){
                if(bestIndividual.getFitness()>selectIndividuals.get(0).getFitness()){
                    bestIndividual=selectIndividuals.get(0).clone();
                    noBetterNum=0;
                }else{
                    noBetterNum++;
                }
            }
            if (this.maxNoBetterNum<=noBetterNum){
                log.info("陷入局部搜索无最优解，退出程序");
//                System.out.println("陷入局部搜索无最优解，退出程序");
                break;
            }
            if ((System.currentTimeMillis()-beginTime)>=this.maxCalTime){
                log.info("超出最大计算时间，退出程序");
                break;
            }
            ArrayList<Individual>offSpring=gaUtils.popCrossoverAndMutation(selectIndividuals,crossoverProbability,mutationProbability,dataManage);
            individuals=offSpring;
        }

        return bestIndividual;
    }

    public static void main(String[] args) {
        GA ga=new GA(10,2,0.8,0.1,100,36000,10);
        String path="D:\\GXprogram\\Doc\\algo\\src\\main\\java\\com\\algo\\FJSP\\GaForFJSP\\ex\\mk10.fjs";
        ProcessDataUtils p=new ProcessDataUtils();
        DataManage dataManage=new DataManage();
        Individual bestIndividual=null;
        ArrayList<Individual>individuals=new ArrayList<Individual>();
        try {
            dataManage= p.readFileToJobList(path);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        try {
            individuals.add(ga.solve(dataManage));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
